package org.example.service;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.YearMonth;
import java.util.*;

/**
 * 贷款分配服务
 * 根据业务规则生成贷款放款计划和逾期数据
 */
@Slf4j
@Service
public class LoanAllocationService {

    // 总放款金额：5000万
    private static final BigDecimal TOTAL_LOAN_AMOUNT = new BigDecimal("50000000");

    // 每笔平均额度：5000元
    private static final BigDecimal AVERAGE_LOAN_AMOUNT = new BigDecimal("5000");

    // 总体逾期率：7%
    private static final double OVERALL_OVERDUE_RATE = 0.07;

    // 2月份逾期率稍高：9%
    private static final double FEBRUARY_OVERDUE_RATE = 0.09;

    // 开始月份：2023年7月
    private static final YearMonth START_MONTH = YearMonth.of(2023, 7);

    // 结束月份：2025年10月
    private static final YearMonth END_MONTH = YearMonth.of(2025, 10);

    // 1-3月份分配比例
    private static final double JAN_RATIO = 0.50;  // 1月占50%
    private static final double FEB_RATIO = 0.30;  // 2月占30%
    private static final double MAR_RATIO = 0.20;  // 3月占20%

    /**
     * 生成完整的放款计划
     */
    public LoanPlan generateLoanPlan() {
        LoanPlan plan = new LoanPlan();

        // 计算总月数
        int totalMonths = calculateTotalMonths();
        log.info("总月数: {}", totalMonths);

        // 计算总笔数
        int totalLoans = TOTAL_LOAN_AMOUNT.divide(AVERAGE_LOAN_AMOUNT, 0, RoundingMode.HALF_UP).intValue();
        log.info("预计总笔数: {}", totalLoans);

        // 生成每月放款计划
        List<MonthlyLoanAllocation> monthlyAllocations = generateMonthlyAllocations(totalLoans, totalMonths);
        plan.setMonthlyAllocations(monthlyAllocations);

        // 生成逾期数据
        List<OverdueData> overdueDataList = generateOverdueData(monthlyAllocations);
        plan.setOverdueDataList(overdueDataList);

        // 统计信息
        plan.setTotalLoans(totalLoans);
        plan.setTotalAmount(TOTAL_LOAN_AMOUNT);
        plan.setStartMonth(START_MONTH);
        plan.setEndMonth(END_MONTH);

        logPlanSummary(plan);

        return plan;
    }

    /**
     * 计算总月数
     */
    private int calculateTotalMonths() {
        YearMonth current = START_MONTH;
        int count = 0;
        while (!current.isAfter(END_MONTH)) {
            count++;
            current = current.plusMonths(1);
        }
        return count;
    }

    /**
     * 生成每月放款分配
     */
    private List<MonthlyLoanAllocation> generateMonthlyAllocations(int totalLoans, int totalMonths) {
        List<MonthlyLoanAllocation> allocations = new ArrayList<>();

        // 计算基础递增系数
        double baseUnit = calculateBaseUnit(totalLoans, totalMonths);

        YearMonth currentMonth = START_MONTH;
        int monthIndex = 0;
        int allocatedLoans = 0;
        
        // 缓存每年1-3月的分配数据，避免重复计算
        Map<Integer, Map<Integer, Integer>> yearlyJanMarAllocations = new HashMap<>();

        while (!currentMonth.isAfter(END_MONTH)) {
            MonthlyLoanAllocation allocation = new MonthlyLoanAllocation();
            allocation.setYearMonth(currentMonth);
            allocation.setMonthIndex(monthIndex);

            // 计算该月基础放款笔数（递增）
            double baseLoans = baseUnit * (monthIndex + 1);
            
            int loanCount;
            int month = currentMonth.getMonthValue();
            int year = currentMonth.getYear();

            // 如果是1-3月，需要特殊处理
            if (month >= 1 && month <= 3) {
                // 检查是否已经计算过该年的1-3月分配
                if (!yearlyJanMarAllocations.containsKey(year)) {
                    // 第一次遇到该年的1-3月，计算三个月的分配
                    Map<Integer, Integer> allocationsForYear = calculateJanMarAllocations(
                        year, baseUnit, monthIndex, totalMonths);
                    yearlyJanMarAllocations.put(year, allocationsForYear);
                }
                
                // 获取该月的分配笔数
                loanCount = yearlyJanMarAllocations.get(year).getOrDefault(month, (int) Math.round(baseLoans));
            } else {
                // 非1-3月，直接使用基础笔数
                loanCount = (int) Math.round(baseLoans);
            }

            // 最后一个月调整，确保总数正确
            if (currentMonth.equals(END_MONTH)) {
                loanCount = totalLoans - allocatedLoans;
            }

            allocation.setLoanCount(loanCount);
            allocation.setLoanAmount(AVERAGE_LOAN_AMOUNT.multiply(new BigDecimal(loanCount)));

            allocatedLoans += loanCount;
            allocations.add(allocation);

            log.debug("{}月 - 放款笔数: {}, 累计: {}", currentMonth, loanCount, allocatedLoans);

            currentMonth = currentMonth.plusMonths(1);
            monthIndex++;
        }

        return allocations;
    }
    
    /**
     * 计算1-3月的分配（基于当年的这三个月）
     */
    private Map<Integer, Integer> calculateJanMarAllocations(int year, double baseUnit, int currentMonthIndex, int totalMonths) {
        Map<Integer, Integer> allocations = new HashMap<>();
        
        YearMonth jan = YearMonth.of(year, 1);
        YearMonth feb = YearMonth.of(year, 2);
        YearMonth mar = YearMonth.of(year, 3);

        // 找到对应的月份索引
        int janIndex = getMonthIndex(jan);
        int febIndex = getMonthIndex(feb);
        int marIndex = getMonthIndex(mar);

        // 如果所有月份都不在范围内，返回空Map
        if (janIndex < 0 && febIndex < 0 && marIndex < 0) {
            return allocations;
        }

        // 计算1-3月的理论总量（基于递增）
        double janBase = janIndex >= 0 ? baseUnit * (janIndex + 1) : 0;
        double febBase = febIndex >= 0 ? baseUnit * (febIndex + 1) : 0;
        double marBase = marIndex >= 0 ? baseUnit * (marIndex + 1) : 0;
        double threeMonthTotal = janBase + febBase + marBase;

        // 按比例分配
        if (janIndex >= 0) {
            allocations.put(1, (int) Math.round(threeMonthTotal * JAN_RATIO));
        }
        if (febIndex >= 0) {
            allocations.put(2, (int) Math.round(threeMonthTotal * FEB_RATIO));
        }
        if (marIndex >= 0) {
            allocations.put(3, (int) Math.round(threeMonthTotal * MAR_RATIO));
        }

        return allocations;
    }

    /**
     * 计算基础单位（用于递增）
     */
    private double calculateBaseUnit(int totalLoans, int totalMonths) {
        // 考虑1-3月特殊分配后的调整
        // 简化计算：使用等差数列求和公式反推
        // 总和 = (首项 + 末项) * 项数 / 2
        // 末项 = 首项 + (项数-1) * 公差
        // 设公差 = 首项，则 总和 = 首项 * 项数 * (项数+1) / 2

        double sum = totalMonths * (totalMonths + 1) / 2.0;
        return totalLoans / sum;
    }

    /**
     * 获取月份在整个计划中的索引
     */
    private int getMonthIndex(YearMonth month) {
        if (month.isBefore(START_MONTH) || month.isAfter(END_MONTH)) {
            return -1;
        }

        YearMonth current = START_MONTH;
        int index = 0;
        while (current.isBefore(month)) {
            current = current.plusMonths(1);
            index++;
        }
        return index;
    }

    /**
     * 生成逾期数据
     * 逻辑：每个月放款后，在接下来的6个月内逾期笔数递增
     */
    private List<OverdueData> generateOverdueData(List<MonthlyLoanAllocation> allocations) {
        List<OverdueData> overdueDataList = new ArrayList<>();

        // 遍历每个放款月份
        for (MonthlyLoanAllocation allocation : allocations) {
            YearMonth loanMonth = allocation.getYearMonth();
            int loanCount = allocation.getLoanCount();

            // 确定该月的目标逾期率
            double targetOverdueRate = (loanMonth.getMonthValue() == 2) ?
                FEBRUARY_OVERDUE_RATE : OVERALL_OVERDUE_RATE;

            // 计算该批次最终逾期笔数
            int finalOverdueCount = (int) Math.round(loanCount * targetOverdueRate);

            // 生成前6期的逾期递增数据
            for (int period = 1; period <= 6; period++) {
                YearMonth checkMonth = loanMonth.plusMonths(period);

                // 不超过结束月份
                if (checkMonth.isAfter(END_MONTH)) {
                    break;
                }

                OverdueData overdueData = new OverdueData();
                overdueData.setLoanMonth(loanMonth);
                overdueData.setCheckMonth(checkMonth);
                overdueData.setPeriod(period);
                overdueData.setTotalLoanCount(loanCount);

                // 逾期笔数按期数递增分配
                // 前6期递增：第1期占总逾期的1/21, 第2期占2/21, ..., 第6期占6/21
                // 总和 = 1+2+3+4+5+6 = 21
                double ratio = period / 21.0;
                int overdueCount = (int) Math.round(finalOverdueCount * ratio);

                // 确保逐期递增
                overdueData.setOverdueCount(overdueCount);
                overdueData.setOverdueRate(loanCount > 0 ?
                    (double) overdueCount / loanCount : 0);

                overdueDataList.add(overdueData);
            }

            // 6期后保持不变，生成后续数据
            YearMonth month7 = loanMonth.plusMonths(7);
            if (!month7.isAfter(END_MONTH)) {
                OverdueData stableData = new OverdueData();
                stableData.setLoanMonth(loanMonth);
                stableData.setCheckMonth(month7);
                stableData.setPeriod(7);
                stableData.setTotalLoanCount(loanCount);
                stableData.setOverdueCount(finalOverdueCount);
                stableData.setOverdueRate(loanCount > 0 ?
                    (double) finalOverdueCount / loanCount : 0);
                stableData.setStable(true);

                overdueDataList.add(stableData);
            }
        }

        return overdueDataList;
    }

    /**
     * 打印计划摘要
     */
    private void logPlanSummary(LoanPlan plan) {
        log.info("========== 贷款分配计划摘要 ==========");
        log.info("时间范围: {} 至 {}", plan.getStartMonth(), plan.getEndMonth());
        log.info("总放款笔数: {}", plan.getTotalLoans());
        log.info("总放款金额: {} 元", plan.getTotalAmount());
        log.info("每笔平均额度: {} 元", AVERAGE_LOAN_AMOUNT);
        log.info("目标逾期率: {}%", OVERALL_OVERDUE_RATE * 100);
        log.info("======================================");

        // 打印每月明细
        log.info("\n每月放款明细:");
        for (MonthlyLoanAllocation allocation : plan.getMonthlyAllocations()) {
            log.info("{}: {} 笔, {} 元",
                allocation.getYearMonth(),
                allocation.getLoanCount(),
                allocation.getLoanAmount());
        }
    }

    /**
     * 获取指定月份的逾期数据
     */
    public List<OverdueData> getOverdueDataForMonth(LoanPlan plan, YearMonth loanMonth) {
        return plan.getOverdueDataList().stream()
            .filter(data -> data.getLoanMonth().equals(loanMonth))
            .sorted(Comparator.comparing(OverdueData::getPeriod))
            .toList();
    }

    /**
     * 贷款计划
     */
    @Data
    public static class LoanPlan {
        private int totalLoans;
        private BigDecimal totalAmount;
        private YearMonth startMonth;
        private YearMonth endMonth;
        //生成每月放款计划
        private List<MonthlyLoanAllocation> monthlyAllocations;
        //生成逾期数据
        private List<OverdueData> overdueDataList;
    }

    /**
     * 每月放款分配
     */
    @Data
    public static class MonthlyLoanAllocation {
        private YearMonth yearMonth;
        private int monthIndex;
        private int loanCount;
        private BigDecimal loanAmount;
    }

    /**
     * 逾期数据
     */
    @Data
    public static class OverdueData {
        // 放款月份
        private YearMonth loanMonth;

        // 检查月份（观察月份）
        private YearMonth checkMonth;

        // 期数（放款后第几个月）
        private int period;

        // 该批次总放款笔数
        private int totalLoanCount;

        // 该期逾期笔数
        private int overdueCount;

        // 该期逾期率
        private double overdueRate;

        // 是否已稳定（第6期后）
        private boolean stable;
    }

    /**
     * 导出为CSV格式（用于分析）
     */
    public String exportMonthlyAllocationToCsv(LoanPlan plan) {
        StringBuilder csv = new StringBuilder();
        csv.append("年月,月份索引,放款笔数,放款金额\n");

        for (MonthlyLoanAllocation allocation : plan.getMonthlyAllocations()) {
            csv.append(String.format("%s,%d,%d,%.2f\n",
                allocation.getYearMonth(),
                allocation.getMonthIndex(),
                allocation.getLoanCount(),
                allocation.getLoanAmount()));
        }

        return csv.toString();
    }

    /**
     * 导出逾期数据为CSV格式
     */
    public String exportOverdueDataToCsv(LoanPlan plan) {
        StringBuilder csv = new StringBuilder();
        csv.append("放款月份,检查月份,期数,总放款笔数,逾期笔数,逾期率,是否稳定\n");

        for (OverdueData data : plan.getOverdueDataList()) {
            csv.append(String.format("%s,%s,%d,%d,%d,%.4f,%s\n",
                data.getLoanMonth(),
                data.getCheckMonth(),
                data.getPeriod(),
                data.getTotalLoanCount(),
                data.getOverdueCount(),
                data.getOverdueRate(),
                data.isStable() ? "是" : "否"));
        }

        return csv.toString();
    }
}

